Skip to content

feat(probe): resolve the workspace the agent-relay CLI selects (phase 2) - #2013

Merged
kjgbot merged 10 commits into
mainfrom
feat/probe-cli-workspace
Oct 29, 2026
Merged

kjgbot merged 10 commits into
mainfrom
feat/probe-cli-workspace

Conversation

@khaliqgant

@khaliqgant khaliqgant commented Oct 28, 2026 •

Copy link
Copy Markdown
Member

Phase 2 of moving the probe core into relay (stacked on #2012). The probe now finds its Relaycast workspace the way the agent-relay CLI does, so relay init's local server and self-hosted servers work with no Cloud sign-in.

What changed

  • relay::workspace::cli: a read-only CLI source. It never writes the CLI's files. It follows the CLI's ladder (resolveWorkspaceSelection + resolveWorkspaceTransport):
    1. RELAY_WORKSPACE_KEY > AGENT_RELAY_WORKSPACE_KEY > RELAY_API_KEY
    2. the project pin <project>/.agentworkforce/relay/workspace-key.json when AGENT_RELAY_PROJECT names the project. A malformed pin falls through. Persisted routes and their route credential (relaycastApiKeyRef → relaycast-credentials.json) get the CLI's checks.
    3. the active entry of $AGENT_RELAY_HOME/workspaces.json. If the file exists but can't be read, resolution fails.
  • relay::workspace::origin_binding: reads relaycast-origins.json using the CLI's PBKDF2-HMAC-SHA256 fingerprint (via the ring already linked; no new dependency). It is tested against packages/contracts/fixtures/relaycast-origin-binding.json, so TS, the broker and the probe all agree. The origin is chosen in this order: persisted route (Agent Relay gateways only; a key bound to another server is refused) → RELAY_BASE_URL → the binding → https://cast.agentrelay.com. http://127.0.0.1:8787-style origins and self-hosted ports and path prefixes are accepted. If the bindings file or RELAY_BASE_URL is malformed, resolution fails closed.
  • How the sources combine: the CLI source wins whenever it names a workspace. The Cloud sign-in is used only when the CLI names nothing. If the CLI's selection can't be used, the daemon has no workspace and logs a warning; it does not fall back to the account. Neither source's key is ever sent to the other's origin (RELAYCAST_URL re-origins only the account's key). Workspace.source records which source the key came from.
  • Refresh/verify/recovery: a key rotated in workspaces.json is picked up by the next 30 s rediscovery and verified. Cloud recovery runs only for Cloud-sourced keys.
  • One workspace per daemon (documented). About 40 call sites treat Core.workspace as the workspace, so per-project workspaces that differ from the machine's active one are a follow-up.
  • Prettier fix: the auto-format bot on feat(probe): import the desktop probe core into crates/probe (phase 1) #2012 reformatted assets that the core embeds verbatim (the OpenCode and Pi plugins, the Claude hook, the sign-in page, fixtures), which broke 2 OpenCode tests. This PR restores them and adds crates/probe/{src,resources,tests/fixtures} to .prettierignore.
  • Docs: docs/probe-move.md (phase 2 done) and crates/probe/docs/relay-core.md (Workspace discovery).

Behaviour change to note

A desktop that is signed in to Cloud and has an active CLI workspace now uses the CLI's workspace for messaging. Uploads still go to the account. This reaches desktop users only in phase 4.

Verification

  • HOME=<empty> cargo test --locked --no-fail-fast in crates/probe: everything passes except the two known ones: cloud::tests::cloud_bearer_reuses_only_a_matching_unexpired_official_cli_session (env-dependent) and live::selected_remote::tests::selected_claude_worker_refreshes_shallow_catalog_and_selected_evidence (flake). 41 new or updated workspace tests cover precedence, origin binding with the shared fixture, malformed files, localhost, persisted routes, the combination rule, rotation, and recovery gating.
  • Live run, fully isolated (scratch HOME, AGENT_RELAY_HOME, app-data dir and socket, env -i; no Cloud sign-in): npx -y agent-relay@next init --yes --tools none --port 8799, then agent-relay-probe relay serve --headless. relay call status.get shows base_url http://127.0.0.1:8799, verification: verified, workspace_name: local. With a corrupted relaycast-origins.json the probe reports state: missing and does not fall back.

🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

View guided diff


Agent Relay sessions

  • claude session ad9499a6 · opened via gh pr create · last active 2026-10-28
  • claude session a7931a00 · contributor · ran gh pr commands · last active 2026-10-29
  • claude session 8ab900cc · contributor · pushed commits · last active 2026-10-29

khaliqgant and others added 2 commits October 28, 2026 09:32
The auto-format bot reformatted the OpenCode and Pi plugins, the Claude
mobile hook, the sign-in page and JSON fixtures that the core embeds with
include_str! and checks verbatim, failing two OpenCode tests. Restore them
from the phase 1 import and ignore crates/probe/{src,resources,tests/fixtures}
in Prettier (docs stay formatted).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The probe now finds its Relaycast workspace the way the CLI does, so local
and self-hosted Relaycast work with no Cloud sign-in:

- relay::workspace::cli mirrors the CLI ladder read-only: RELAY_WORKSPACE_KEY
  > AGENT_RELAY_WORKSPACE_KEY > RELAY_API_KEY, the AGENT_RELAY_PROJECT pin
  (with its persisted route and route credential), then the active entry of
  $AGENT_RELAY_HOME/workspaces.json.
- relay::workspace::origin_binding reads relaycast-origins.json with the
  CLI's PBKDF2-SHA256 fingerprint (shared fixture with TS and the broker);
  a bound key goes only to its server, unbound to cast.agentrelay.com,
  RELAY_BASE_URL is the explicit destination, and malformed files fail closed.
- The CLI source wins when it names a workspace; the Cloud sign-in is the
  fallback. A refused CLI selection leaves no workspace rather than falling
  back, and neither source's key goes to the other's origin.
- Workspace.source records the source; Cloud recovery only runs for Cloud
  keys, and a key rotated in workspaces.json is picked up on refresh.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review.

Recent review info
Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 29c0f09c-1962-41ae-b67a-81603a8d2b2d
Commits

Reviewing files that changed from the base of the PR and between ea940dc and f64c01a.

Files selected for processing: 5
  • CHANGELOG.md
  • crates/probe/src/relay/messaging.rs
  • crates/probe/src/relay/workspace.rs
  • crates/probe/src/relay/workspace/cli.rs
  • crates/probe/src/relay/workspace/cli/tests.rs

Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 3 remain after this review.

Walkthrough

The probe now selects CLI workspaces before Cloud workspaces. It validates CLI credentials, routes, and origins, records workspace sources, and applies Cloud key recovery only to Cloud-sourced workspaces.

Changes: Workspace Resolution

Layer / File(s) Summary
Workspace source and origin bindings
crates/probe/src/relay/workspace.rs, crates/probe/src/relay/workspace/origin_binding.rs
Workspace now records its source. Origin-binding files map credential fingerprints to validated server origins.
CLI selection and transport validation
crates/probe/src/relay/workspace/cli.rs, crates/probe/src/relay/workspace/cli/tests.rs
The resolver selects credentials from CLI environment variables, an eligible project pin, or the active store entry. It validates persisted routes and credential-origin matches. Tests cover selection, transport, file errors, and path handling.
Daemon discovery and workspace identity
crates/probe/src/relay/daemon.rs, crates/probe/src/relay/messaging.rs, crates/probe/src/relay/workspace.rs, crates/probe/src/relay/{desktop_identity,spawn,state}.rs, crates/probe/src/relay/spawn/control/tests.rs
The daemon resolves CLI workspaces, reports changed discovery problems, and includes workspace source in activity messages. Messaging credential selection requires matching workspace origin and account key.
Cloud recovery and release documentation
crates/probe/src/relay/account/workspace_recovery.rs, crates/probe/src/relay/workspace_recovery_tests.rs, docs/probe-move.md, CHANGELOG.md, crates/probe/docs/agent-relay-probe.md
Key recovery applies only to Cloud-sourced workspaces. Tests cover CLI key refresh and refusal without Cloud fallback. The migration document and changelog describe workspace selection.

Priority: Normal

Estimated code review effort: 4 (Complex) | ~45 minutes

Change: Feature

Sequence Diagram(s)

sequenceDiagram
  participant Daemon as daemon::rediscover_workspace
  participant Resolver as workspace::resolve
  participant CLI as cli::resolve
  participant Bindings as origin_binding::read_bindings
  Daemon->>Resolver: resolve with configured home
  Resolver->>CLI: check CLI workspace selection
  CLI->>Bindings: read credential origin bindings
  Bindings-->>CLI: return validated bindings
  CLI-->>Resolver: return workspace, no selection, or error
  Resolver-->>Daemon: return selected workspace or Cloud fallback
Loading

Possibly related PRs

Merge Risk: Minimal · up to f64c0

No issue identified in the reviewed workspace-selection changes requires resolution before merging.

Pre-merge checks: Passed 3 · Failed 2

Failed Failed checks: 2 warnings

Check name Status Explanation Resolution
Description check Warning The description provides a detailed summary and verification results, but it omits the required RelayFlow Proof section and does not provide the required change type or exactly one RelayFlow case. The… Add the required RelayFlow Proof section. Set Change type to feature or bugfix, and specify exactly one case under tests/relayflows/cases/<case-id>/. Add the template Test Plan checkboxes and mark the applicable items.
Docstring Coverage Warning Docstring coverage is 60.53% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documenta… Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 3 passed
Check name Status Explanation
Title check Passed The title clearly identifies the main change: probe workspace resolution now follows the workspace selected by the agent-relay CLI. The phase designation is relevant and concise.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Full details: Description check

Explanation

The description provides a detailed summary and verification results, but it omits the required RelayFlow Proof section and does not provide the required change type or exactly one RelayFlow case. The test plan is also not presented using the template checkboxes.

Resolution

Add the required RelayFlow Proof section. Set Change type to feature or bugfix, and specify exactly one case under tests/relayflows/cases/&lt;case-id&gt;/. Add the template Test Plan checkboxes and mark the applicable items.

Full details: Docstring Coverage

Explanation

Docstring coverage is 60.53% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 38 functions across 12 files. (1 skipped: 1 unsupported.)

Resolution

Write docstrings for the functions missing them to satisfy the coverage threshold.

Finishing Touches: 💡 1
📝 Generate docstrings: 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

I’m a rabbit with a key in my paw,
I hop through the workspace files I saw.
Pins and origins line up just right,
Cloud waits when no CLI takes flight.
I thump for safe routes, then burrow away.

Comment @coderabbitai help to get the list of available commands.

@khaliqgant
khaliqgant marked this pull request as ready for review October 29, 2026 02:18
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-29T02:20:52.499937Z ea940dc Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ea940dc8a5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread crates/probe/src/relay/workspace/cli.rs Resolved
Comment thread crates/probe/src/relay/workspace.rs Resolved

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 potential issues.

Devin Review

Comment thread crates/probe/src/relay/workspace/cli.rs Resolved
Comment thread crates/probe/src/relay/workspace.rs Resolved
@greptile-apps

greptile-apps Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

RetriggerView in GreptileConfidence Score: 5/5

[High impact] The PR appears safe to merge; the remaining Windows test gap is non-blocking.

Summary

The probe now selects the CLI's workspace before the Cloud account's workspace.

  • The probe uses the CLI-selected workspace before the signed-in Cloud workspace.
  • Messaging checks that the selected workspace matches the account identity.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Discover workspace] --> B{CLI selects a key?}
  B -->|Yes| C{CLI settings usable?}
  C -->|Yes| D[Use CLI workspace]
  C -->|No| E[No workspace; report warning]
  B -->|No| F[Use Cloud workspace if signed in]
  D --> G[Verify key]
  F --> G
  G --> H{Cloud key refused?}
  H -->|Yes| I[Try Cloud recovery]
  H -->|No| J[Keep normal refresh behavior]
Loading

Reviews (2) · Last reviewed commit: "fix(probe): tie person-send proof to the..." · Reviewed by Greptile

Comment thread crates/probe/src/relay/workspace.rs Resolved
…the CLI store reader

- Roster and recipient lookups for sends made as you now require the
  daemon's workspace to be the signed-in account's own workspace (same
  key on the identity's origin), not just the same base URL, so a CLI
  workspace on cast.agentrelay.com is never taken as proof about sends
  in the account's workspace.
- workspaces.json: a top-level null, or a non-string `active` or entry
  `key`, is an error as in the CLI; a null `workspaces` stays empty, as
  the CLI's `parsed.workspaces ?? {}` reads it.
- Windows: strip the \\?\ and \\?\UNC\ verbatim prefixes before hashing
  credential refs, as Node's realpathSync.native prints paths; add a
  Windows test pinned to the CLI's algorithm.
- CHANGELOG: note the probe's CLI-first workspace selection.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
kjgbot pushed a commit that referenced this pull request Oct 29, 2026
…waiting

- settings.set asks each settings extension to refuse a change before
  anything is written (SettingsExtension::refuse), so a batch refused for
  history_retention_days (history_unavailable without history, an invalid
  value with it) saves none of its other settings.
- Without history, send_cloud never refuses: a cloud-shaped id can name a
  stopped local Devin session, so the local Waiting stands.
- Run the retained-Codex-row admission test in both builds; only the
  cloud-catalog helper and test stay history-only.
- README: Cloud sign-in is the current workspace path, CLI resolution comes
  in #2013; test commands keep RUSTUP_HOME/CARGO_HOME before hiding HOME.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@kjgbot kjgbot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CI green at f64c01a and every review thread is addressed and resolved.

@kjgbot
kjgbot merged commit 4b93413 into main Oct 29, 2026
90 of 91 checks passed
kjgbot pushed a commit that referenced this pull request Oct 29, 2026
Keeps both the no-history build notes and #2013's CLI workspace
resolution; the README now describes CLI resolution as current.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants